<?php
/**
 * ANTFW database access layer
 *
 * @author $AUTHOR$
 * @since $DATE$
 * @version $ID$
 */
defined('ANTFW_ROOT') || die('Access deined');

final class db {
    /**
     * links, contain default & master & slave
     *
     * @access private
     * @keyword static
     */
    private static $_links = array(
        'default' => NULL,
        'master'  => NULL,
        'slave'   => NULL
    ); 

    /**
     * resource
     *
     * @access private
     */
    private $_resource = NULL;

    /**
     * configure, contain default & master & slave
     *
     * @access static private
     * @keyword static
     */
    public static $config = array(
        'default' => array(),
        'master'  => array(),
        'slave'   => array()
    );

    /**
     * prefix
     *
     * @access public
     * @keyword static
     * @modified for setting to private static by Andrew li on 15:05 11/20/11
     */
    public static $prefix = NULL;

    /**
     * use link, can be: default, master, slave, separate
     *
     * @access public
     */
    public $use = 'default';

    /**
     * use a link once
     *
     * @access public
     */
    public $once = NULL;

    /**
     * debug switch
     *
     * @access public
     */
    public $debug = FALSE;

    /**
     * logs
     *
     * @access public
     */
    public $logs = NULL;


    /**
     * execute query string and return result 
     *
     * @access public
     * @param string $sql
     * @return mixed
     */
    public function q($sql) {
        if (!$sql)
            return FALSE;

        // connect
        if (isset(self::$_links[$this->once])) {
            $type = $this->once;
            $this->once = NULL;
        } elseif ($this->use == 'separate') {
            // check query type, if select/set/show
            $sql = trim($sql);
            if (0 === stripos($sql, 'SELECT') || 0 === stripos($sql, 'SET') || 0 === stripos($sql, 'SHOW'))
                $type = 'master';
            else
                $type = 'slave';
        } else
            $type = $this->use;

        $this->_log('connect type: '. $type, FALSE);

        if (!$this->_connect($type))
            return FALSE;

        // replace @__
        $sql = str_replace('@__', self::$prefix, $sql);

        // execute
        $this->_resource = @mysql_query($sql, self::$_links[$type]);

        if (FALSE === $this->_resource)
            return $this->_log(mysql_error(), FALSE);

        if ($id = mysql_insert_id(self::$_links[$type]))
            return $id;

        if (is_resource($this->_resource) && get_resource_type($this->_resource) == 'mysql result') {
            $result = array();

            while($row = mysql_fetch_assoc($this->_resource))
                $result[] = $row;

            return $result;
        }

        return TRUE;
    }


    /**
     * insert into table
     *
     * @access public 
     * @param string $table
     * @param array $data
     * @param integer $allow_duplicate
     * @return mixed 
     */
    public function i($table, $data, $allow_duplicate = 0) {
        if (!$table)
            return FALSE;

        if (!$data)
            return FALSE;

        // if not allow duplicate
        if (!$allow_duplicate && $this->n($table, $data))
            return TRUE;

        $data = $this->_parseData($data);
        if (is_array($data))
            $data = implode(', ', $data);

        return $this->q('INSERT INTO '. $table. ' SET '. $data);
    }


    /**
     * update record 
     *
     * @access public 
     * @param string $table
     * @param array $data
     * @param mixed $condition
     * @return boolean
     */
    public function u($table, $data, $condition = NULL) {
        if (!$table)
            return FALSE;

        $data = $this->_parseData($data);
        if (is_array($data))
            $data = implode(', ', $data);
        if (!$data)
            return FALSE;

        $condition = $this->_parseData($data, 1);
        if (is_array($condition))
            $condition = implode(' AND ', $condition);

        return $this->q('UPDATE '. $table. ' SET '. $data. ($condition? ' WHERE '. $condition: ''));
    }


    /**
     * delete record
     *
     * @access public
     * @param string $table
     * @param mixed $condition
     * @return boolean
     */
    public function d($table, $condition = NULL) {
        if (!$table)
            return FALSE;

        $condition = $this->_parseData($data, 1);
        if (is_array($condition))
            $condition = implode(' AND ', $condition);

        return $this->q('DELETE FROM '. $table. ($condition? ' WHERE '. $condition: ''));
    }


    /**
     * select record
     *
     * @access public
     * @param string $table
     * @param string $fields
     * @param mixed $condition
     * @return mixed
     */
    public function s($table, $fields, $condition = NULL) {
        if (!$table)
            return FALSE;

        $condition = $this->_parseData($data, 1);
        if (is_array($condition))
            $condition = implode(' AND ', $condition);

        return $this->q('SELECT '. $fields. ' FROM '. $table. ($condition? ' WHERE '. $condition: ''));
    }


    /**
     * Select one record/field
     *
     * @access public
     * @param string $sql
     * @param string $field
     * @return mixed
     */
    public function r($sql, $field = NULL) {
        $result = $this->q($sql);

        if (FALSE === $result)
            return FALSE;

        if (isset($result[0])) {
            if (NULL !== $field)
                return isset($result[0][$field])? $result[0][$field]: NULL;
            else
                return $result[0];
        }

        return NULL;
    }


    /**
     * Count record
     *
     * @access public
     * @param string $table
     * @param mixed $condition
     * @return mixed
     */
    public function n($table, $condition = NULL) {
        if (!$table)
            return FALSE;

        $condition = $this->_parseData($data, 1);
        if (is_array($condition))
            $condition = implode(' AND ', $condition);

        $result = $this->q('SELECT COUNT(1) AS n FROM '. $table. ($condition? ' WHERE '. $condition: ''));

        if (FALSE === $result)
            return FALSE;

        return $return[0]['n'];
    }


    /**
     * free
     *
     * @access public
     * @return void
     */
    public function free() {
        if (is_resource($this->_resource) && get_resource_type($this->_resource) == 'mysql result')
            mysql_free_result($this->_resource);

        $this->_resource = NULL;

        $types = array_keys(self::$_links);
        foreach ($types as $t)
            if (is_resource(self::$_links[$type]) && get_resource_type(self::$_links[$type]) == 'mysql link')
                mysql_close(self::$_links[$type]);
    }


    /**
     * connect
     *
     * @access private
     * @param string $type: default, master, slave
     * @return void
     */
    private function _connect($type) {
        if (is_resource(self::$_links[$type]) && get_resource_type(self::$_links[$type]) == 'mysql link')
            return TRUE;

        if ($type == 'default')
            $config = self::$config['default'];
        else
            $config = array_rand(self::$config[$type]);

        self::$_links[$type] = @mysql_connect($config['host']. ':'. ($config['port']? $config['port']: 3306), $config['user'], $config['password']);

        if (is_resource(self::$_links[$type]) && get_resource_type(self::$_links[$type]) == 'mysql link') {
            if (!@mysql_select_db($config['dbname'], self::$_links[$type]))
                return FALSE;

            self::$prefix = $config['prefix'];
            return TRUE;
        }

        return FALSE;
    }


    /**
     * Parse data
     *
     * @access private
     * @param mixed $data
     * @param integer $isCondition
     * @return mixed
     */
    private function _parseData($data, $isCondition = 0) {
        if (is_string($data))
            return $data;

        if (!is_array($data))
            return NULL;

        $arr = array();
        foreach ($data as $k=>$d) {
            if (NULL === $d) {
                if ($isCondition)
                    $arr[] = "$k IS NULL";
                else
                    $arr[] = "$k = NULL";
            } else
                $arr[] = "$k = '". mysql_escape_string($d). "'";

        }

        return $arr;
    }


    /**
     * Log debug
     *
     * @access private
     * @param mixed $data
     * @param mixed $return
     * @return $return
     */
    private function _log($data, $return = TRUE) {
        if ($this->debug)
            $this->logs[] = print_r($data, TRUE);

        return $return;
    }
}
